
#include "av_player.h"
#include "log_print.h"

OHNativeWindow* AvPlayer::nativeWin_ = nullptr;
std::shared_ptr<JSObserver> AvPlayer::observer_ = nullptr;
std::shared_ptr<JSObserver> AvPlayer::durationObserver_ = nullptr;
std::shared_ptr<JSObserver> AvPlayer::posObserver_ = nullptr;
std::map<AVPlayerOnInfoType, std::function<void(OH_AVPlayer *, OH_AVFormat *, AvPlayer::PlayerPara *)>>
    AvPlayer::stateChange_ = {
        {AV_INFO_TYPE_STATE_CHANGE, [](OH_AVPlayer *player, OH_AVFormat *infoBody, AvPlayer::PlayerPara *playerPara) {
            OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_STATE, &playerPara->state);
            OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_STATE_CHANGE_REASON, &playerPara->stateChangeReason);
            playerPara->avState = static_cast<AVPlayerState>(playerPara->state);
            HandleStateChange(player, playerPara->avState);
        }},
        {AV_INFO_TYPE_SEEKDONE, [](OH_AVPlayer *player, OH_AVFormat *infoBody, AvPlayer::PlayerPara *playerPara) {
            int32_t value = -1;
            OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_SEEK_POSITION, &value);
        }},
        {AV_INFO_TYPE_SPEEDDONE, [](OH_AVPlayer *player, OH_AVFormat *infoBody, AvPlayer::PlayerPara *playerPara) {
            int32_t value = -1;
            OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_PLAYBACK_SPEED, &value);
            playerPara->speed = static_cast<AVPlaybackSpeed>(value);
        }},
        {AV_INFO_TYPE_BITRATEDONE, [](OH_AVPlayer *player, OH_AVFormat *infoBody, AvPlayer::PlayerPara *playerPara) {
            int32_t value = -1;
            OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_BITRATE, &value);
            playerPara->bitRate = static_cast<uint32_t>(value);
        }},
        {AV_INFO_TYPE_POSITION_UPDATE, [](OH_AVPlayer *player, OH_AVFormat *infoBody, AvPlayer::PlayerPara *playerPara)
        {
            int32_t value = -1;
            OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_CURRENT_POSITION, &value);
            UvQueue::NapiArgsGenerator gentor = [value](napi_env env, int& argc, napi_value* argv) {
                argc = 1;
                napi_value pos;
                napi_create_int32(env, value, &pos);
                argv[0] = pos;
            };
            posObserver_->AsyncCall(gentor);
        }},
        {AV_INFO_TYPE_MESSAGE, [](OH_AVPlayer *player, OH_AVFormat *infoBody, AvPlayer::PlayerPara *playerPara) {
            int32_t value = -1;
            OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_MESSAGE_TYPE, &value);
        }},
        {AV_INFO_TYPE_VOLUME_CHANGE, [](OH_AVPlayer *player, OH_AVFormat *infoBody, AvPlayer::PlayerPara *playerPara) {
            OH_AVFormat_GetFloatValue(infoBody, OH_PLAYER_VOLUME, &playerPara->volume);
        }},
        {AV_INFO_TYPE_RESOLUTION_CHANGE, [](
            OH_AVPlayer *player, OH_AVFormat *infoBody, AvPlayer::PlayerPara *playerPara) {
            OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_VIDEO_WIDTH, &playerPara->width);
            OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_VIDEO_HEIGHT, &playerPara->height);
        }},
        {AV_INFO_TYPE_BUFFERING_UPDATE, [](
            OH_AVPlayer *player, OH_AVFormat *infoBody, AvPlayer::PlayerPara *playerPara) {
            int32_t value = -1;
            OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_BUFFERING_TYPE, &value);
            OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_BUFFERING_VALUE, &playerPara->bufferValue);
            playerPara->bufferType = static_cast<AVPlayerBufferingType>(value);
        }},
        {AV_INFO_TYPE_BITRATE_COLLECT, [](OH_AVPlayer *player, OH_AVFormat *infoBody, AvPlayer::PlayerPara *playerPara)
        {
            BitrateCollect(infoBody, playerPara);
        }},
        {AV_INFO_TYPE_INTERRUPT_EVENT, [](OH_AVPlayer *player, OH_AVFormat *infoBody, AvPlayer::PlayerPara *playerPara)
        {
            OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_AUDIO_INTERRUPT_TYPE, &playerPara->interruptType);
            OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_AUDIO_INTERRUPT_FORCE, &playerPara->interruptForce);
            OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_AUDIO_INTERRUPT_HINT, &playerPara->interruptHint);
        }},
        {AV_INFO_TYPE_DURATION_UPDATE, [](OH_AVPlayer *player, OH_AVFormat *infoBody, AvPlayer::PlayerPara *playerPara)
        {
            OH_AVFormat_GetLongValue(infoBody, OH_PLAYER_DURATION, &playerPara->duration);
            if (!durationObserver_) {
             return;
            }
            UvQueue::NapiArgsGenerator gentor = [playerPara](napi_env env, int& argc, napi_value* argv) {
                argc = 1;
                napi_value duration;
                napi_create_int64(env, playerPara->duration, &duration);
                argv[0] = duration;
            };
            durationObserver_->AsyncCall(gentor);
        }},
        {AV_INFO_TYPE_IS_LIVE_STREAM, [](OH_AVPlayer *player, OH_AVFormat *infoBody, AvPlayer::PlayerPara *playerPara)
        {
            int32_t value = -1;
            OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_IS_LIVE_STREAM, &value);
        }},
        {AV_INFO_TYPE_AUDIO_OUTPUT_DEVICE_CHANGE, [](
            OH_AVPlayer *player, OH_AVFormat *infoBody, AvPlayer::PlayerPara *playerPara) {
                int32_t value = -1;
                OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_AUDIO_DEVICE_CHANGE_REASON, &value);
        }},
        {AV_INFO_TYPE_TRACKCHANGE, [](OH_AVPlayer *, OH_AVFormat *, AvPlayer::PlayerPara *) {}},
        {AV_INFO_TYPE_TRACK_INFO_UPDATE, [](OH_AVPlayer *, OH_AVFormat *, AvPlayer::PlayerPara *) {}},
        {AV_INFO_TYPE_SUBTITLE_UPDATE, [](OH_AVPlayer *, OH_AVFormat *, AvPlayer::PlayerPara *) {}},
        {AV_INFO_TYPE_EOS, [](OH_AVPlayer *, OH_AVFormat *, AvPlayer::PlayerPara *) {}}
    };

void AvPlayer::Export(napi_env env, napi_value exports)
{
    napi_value xInstance = nullptr;
    napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &xInstance);
    if (xInstance == nullptr) {
        LOGE("export xComponent failed");
        return;
    }
    OH_NativeXComponent *nativeXCpt = nullptr;
    napi_unwrap(env, xInstance, reinterpret_cast<void **>(&nativeXCpt));
    if (nativeXCpt == nullptr) {
        LOGE("unwrap native xComponent failed");
        return;
    }
    callback_.OnSurfaceCreated = OnSurfaceCreated;
    callback_.OnSurfaceDestroyed = OnSurfaceDestroyed;
    OH_NativeXComponent_RegisterCallback(nativeXCpt, &callback_);
}

void AvPlayer::OnSurfaceCreated(OH_NativeXComponent *component, void *window)
{
    (void)component;
    LOGI("on surface created");
    if (window == nullptr) {
        LOGE("window is null");
        return;
    }
    nativeWin_ = static_cast<OHNativeWindow *>(window);
}

void AvPlayer::OnSurfaceDestroyed(OH_NativeXComponent *component, void *window)
{
    (void)component;
    (void)window;
    LOGI("on surface destroyed");
    nativeWin_ = nullptr;
}

void AvPlayer::HandleStateChange(OH_AVPlayer *player, AVPlayerState state)
{
    int32_t ret = -1;
    switch (state) {
        case AV_IDLE:
            LOGI("idle");
            break;
        case AV_INITIALIZED:
            ret = OH_AVPlayer_SetVideoSurface(player, nativeWin_);
            LOGI("set surface:%{public}d", ret);
            ret = OH_AVPlayer_Prepare(player);
            LOGI("prepare ret:%{public}d", ret);
            break;
        case AV_PREPARED:
            ret = OH_AVPlayer_Play(player);
            LOGI("play ret:%{public}d", ret);
            break;
        case AV_PLAYING:
            LOGI("playing");
            break;
        case AV_PAUSED:
            LOGI("paused");
           break;
        case AV_STOPPED:
            LOGI("release ret:%{public}d", ret);
            break;
        case AV_COMPLETED:
        {
             if (observer_) {
                observer_->AsyncCall();
            }
            LOGI("stop ret:%{public}d", ret);
            break;
        }
        default:
            break;
    }
}

void AvPlayer::OnInfoCallback(OH_AVPlayer *player, AVPlayerOnInfoType type, OH_AVFormat *infoBody, void *userData)
{
    PlayerPara *playerPara = reinterpret_cast<PlayerPara *>(userData);
    if (playerPara == nullptr || player == nullptr) {
        LOGE("invalid args");
        return;
    }
    stateChange_[type](player, infoBody, playerPara);
}

void AvPlayer::BitrateCollect(OH_AVFormat *infoBody, PlayerPara *playerPara)
{
    uint8_t *bitRates = nullptr;
    size_t size = 0;
    OH_AVFormat_GetBuffer(infoBody, OH_PLAYER_BITRATE_ARRAY, &bitRates, &size);
    playerPara->bitRatesSize = size / sizeof(uint32_t);
    LOGI("BITRATE_COLLECT bytes size: %{public}zu, size: %{public}zu", size, playerPara->bitRatesSize);
    if (playerPara->bitRatesSize <= 0) {
        LOGI("invalid bitRatesSize");
        return;
    }
    playerPara->bitRates = new uint32_t[playerPara->bitRatesSize];
    if (playerPara->bitRates == nullptr || bitRates == nullptr) {
        LOGI("bitRates is nullptr");
        return;
    }
    memcpy((void *)(playerPara->bitRates), (void *)bitRates, playerPara->bitRatesSize * sizeof(uint32_t));
    for (size_t i = 0; i < playerPara->bitRatesSize; i++) {
        LOGI("bitRates[%{public}zu]: %{public}u", i, *(playerPara->bitRates + i));
    }
}

void AvPlayer::OnErrorCallback(OH_AVPlayer *player, int32_t errorCode, const char *errorMsg, void *userData)
{
    LOGI("errorCode: %{public}d ,errorMsg: %{public}s", errorCode, errorMsg);
    PlayerPara *playerPara = reinterpret_cast<PlayerPara *>(userData);
    if (playerPara == nullptr || player == nullptr) {
        LOGE("invalid args");
        return;
    }
    playerPara->errorCode = errorCode;
}

void AvPlayer::Play(int32_t fd, int64_t size)
{
    OH_AVPlayer* player = OH_AVPlayer_Create();
    if (player == nullptr) {
        LOGE("player nullptr");
        return;
    }
    para_ = new PlayerPara();
    para_->player = player;
    auto ret = OH_AVPlayer_SetOnInfoCallback(player, OnInfoCallback, para_);
    LOGI("set info callback ret:%{public}d", ret);
    
    ret = OH_AVPlayer_SetOnErrorCallback(player, OnErrorCallback, para_);
    if (ret != AV_ERR_OK) {
        LOGE("set error callback ret:%{public}d", ret);
        return;
    }
    ret = OH_AVPlayer_SetFDSource(player, fd, 0, size);
    if (ret != AV_ERR_OK) {
        LOGE("set fd source ret:%{public}d", ret);
        return;
    }
    OH_AudioStream_Usage streamUsage = OH_AudioStream_Usage::AUDIOSTREAM_USAGE_UNKNOWN;
    ret = OH_AVPlayer_SetAudioRendererInfo(player, streamUsage);
    if (ret != AV_ERR_OK) {
        LOGE("set renderer info ret:%{public}d", ret);
        return;  
    }
    OH_AudioInterrupt_Mode interruptMode = OH_AudioInterrupt_Mode::AUDIOSTREAM_INTERRUPT_MODE_INDEPENDENT;
    ret = OH_AVPlayer_SetAudioInterruptMode(player, interruptMode);
    if (ret != AV_ERR_OK) {
        LOGE("set interrupt mode ret:%{public}d", ret);
    }
}

void AvPlayer::PlayCon()
{
    if (!para_) {
        LOGE("player nullptr");
        return;
    }
    auto ret = OH_AVPlayer_Play(para_->player);
    LOGI("play ret:%{public}d", ret);
}

void AvPlayer::Pause()
{
    if (!para_) {
        LOGE("player nullptr");
        return;
    }
    auto ret = OH_AVPlayer_Pause(para_->player);
    LOGI("play ret:%{public}d", ret);
}

void AvPlayer::RegOnFinish(std::shared_ptr<JSObserver> observer)
{
    if (observer_) {
        return;
    }
    observer_ = std::move(observer);
}

void AvPlayer::RegDuration(std::shared_ptr<JSObserver> observer)
{
    if (durationObserver_) {
        return;
    }
    durationObserver_ = std::move(observer);
}

void AvPlayer::RegPos(std::shared_ptr<JSObserver> observer)
{
    if (posObserver_) {
        return;
    }
    posObserver_ = std::move(observer);
}

